
package us.hornerscorners.lollipop.hapi;

/*
* #%L
 * * LollipopRouter
 * *
 * %%
 * Copyright (C) 2013 - 2014 Jim Horner
 * *
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import java.util.List;
import java.util.Map;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.HapiContext;

import ca.uhn.hl7v2.model.Message;

import ca.uhn.hl7v2.protocol.ReceivingApplication;
import ca.uhn.hl7v2.protocol.ReceivingApplicationException;

import com.google.common.collect.Lists;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import us.hornerscorners.lollipop.config.RoutingConfig;


/**
 * Class description
 *
 *
 * @version        v1.0, 2014-09-06
 * @author         Jim Horner
 */
public class RoutingApplication implements ReceivingApplication {

    /** Field description */
    private final List<IClient> handlers;

    /** Field description */
    private final HapiContext hapiContext;

    /** Field description */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Field description */
    private final List<RoutingConfig> routings;

    /** Field description */
    private final Message2RoutingTransformer transformer;

    /**
     * Constructs ...
     *
     *
     *
     * @param hapiContext
     * @param routings
     */
    public RoutingApplication(HapiContext hapiContext, List<RoutingConfig> routings) {

        super();

        this.hapiContext = hapiContext;
        this.routings = Lists.newArrayList(routings);
        this.transformer = new Message2RoutingTransformer();

        this.handlers = Lists.newArrayList(new HohClient(),
                                           new LlpClient(this.hapiContext));
    }

    /**
     * Method description
     *
     *
     * @param msg
     *
     * @return
     */
    @Override
    public boolean canProcess(Message msg) {

        RoutingConfig msgConfig = findRoutingConfig(msg);

        String name = "";

        if (msgConfig != null) {

            name = msgConfig.getName();
        }

        logger.debug("canProcess LLP message {}-{}", msgConfig != null, name);

        return msgConfig != null;
    }

    /**
     * Method description
     *
     *
     * @param msg
     *
     * @return
     */
    protected RoutingConfig findRoutingConfig(Message msg) {

        RoutingConfig result = null;

        RoutingConfig msgRouting = this.transformer.apply(msg);

        for (RoutingConfig routing : this.routings) {

            if (isConfigMatch(routing, msgRouting)) {

                result = routing;

                break;
            }
        }

        return result;
    }

    /**
     * Method description
     *
     *
     * @param r0
     * @param r1
     *
     * @return
     */
    private boolean isConfigMatch(RoutingConfig r0, RoutingConfig r1) {

        return isMatch(r0.getMessageType(), r1.getMessageType())
               && isMatch(r0.getTriggerEvent(), r1.getTriggerEvent())
               && isMatch(r0.getVersion(), r1.getVersion())
               && isMatch(r0.getReceivingApplication(), r1.getReceivingApplication())
               && isMatch(r0.getReceivingFacility(), r1.getReceivingFacility())
               && isMatch(r0.getSendingApplication(), r1.getSendingApplication())
               && isMatch(r0.getSendingFacility(), r1.getSendingFacility());
    }

    /**
     * Method description
     *
     *
     * @param attr1
     * @param attr2
     *
     * @return
     */
    private boolean isMatch(String attr1, String attr2) {

        return ((attr1 == null) || attr1.equals(attr2));
    }

    /**
     * Method description
     *
     *
     * @param msg
     * @param map
     *
     * @return
     *
     * @throws HL7Exception
     * @throws ReceivingApplicationException
     */
    @Override
    public Message processMessage(Message msg, Map<String, Object> map)
            throws ReceivingApplicationException, HL7Exception {

        RoutingConfig msgConfig = findRoutingConfig(msg);

        if (msgConfig == null) {

            throw new ReceivingApplicationException(
                "Unable to process message. No matching routing config.");
        }

        String endpoint = msgConfig.getEndpoint();

        IClient client = null;

        for (IClient handler : this.handlers) {

            if (handler.canHandle(endpoint)) {

                client = handler;
            }
        }

        if (client == null) {

            String err =
                String.format("Unable to process message. No handler available for %s.",
                              endpoint);

            throw new ReceivingApplicationException(err);
        }

        logger.info("Processing LLP message {}", msgConfig.getEndpoint());
        logger.debug("Request Message {}", msg.encode());

        Message result = client.sendMessage(msgConfig, msg);

        logger.debug("Response Message {}", result.encode());

        return result;
    }
}
